#include <bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
const int BASE = 3e5; // 哈希基数，用于处理负数索引
vector<int> a[MAXN];  // 存储矩阵数据

char str[MAXN];         // 临时存储输入字符串
int v[MAXN], sum[MAXN]; // v: 列累计值，sum: 前缀和数组
int t[MAXN * 2];        // 哈希表，记录前缀和出现次数

void Solve() {
  int n, m; // 矩阵行数和列数
  scanf("%d%d", &n, &m);

  // 根据行列大小决定存储方式（行列转置优化）
  if (n <= m)
    for (int i = 1; i <= n; ++i)
      a[i].resize(m + 1);
  else
    for (int i = 1; i <= m; ++i)
      a[i].resize(n + 1);

  // 读取矩阵数据，'#'为1，其他为-1
  for (int i = 1; i <= n; ++i) {
    scanf("%s", str + 1);
    for (int j = 1; j <= m; ++j) {
      int o =(str[j] == '#' ? 1 : -1);
      if (n <= m)
        a[i][j] = o;
      else
        a[j][i] = o; // 行列转置
    }
  }

  if (n > m)
    swap(n, m);      // 保证n是较小的维度
  long long ans = 0; // 结果计数器

  // 枚举所有可能的行区间[up,down]
  for (int up = 1; up <= n; ++up) {
    memset(v, 0, sizeof(v)); // 重置列累计值

    for (int down = up; down <= n; ++down) {
      // 累计当前行区间的列值
      for (int j = 1; j <= m; ++j)
        v[j] += a[down][j];
      // 将行区间内的列值垂直累加，转化为一维数组
      // v[j] 实际上存储的是该列在行方向上的总和

      // 计算前缀和并统计子矩阵数量
      sum[0] = 0;
      t[sum[0] + BASE]++; // 初始化哈希表

      for (int j = 1; j <= m; ++j) {
        sum[j] = sum[j - 1] + v[j]; // 计算前缀和
        ans += t[sum[j] + BASE];    // 统计和为0的子矩阵
        t[sum[j] + BASE]++; // 更新哈希表数组，统计每个前缀和值的出现次数，
      }

      // 重置哈希表
      for (int j = 0; j <= m; ++j)
        // t[sum[j] + BASE]--;
        t[sum[j] + BASE] = 0;
    }
  }

  printf("%lld\n", ans);

  // 清理数据，为下一个样例做准备
  for (int i = 1; i <= n; ++i)
    a[i].clear();
}

int main() {
  int T;
  scanf("%d", &T);
  while (T--)
    Solve(); // 处理每个测试用例
  return 0;
}